﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace CRL.Data.LambdaQuery
{
    public interface IWindowFuncExpressionOver<T, TResult> : IQuery
    {
        IWindowFuncExpressionOver<T, TResult> PartitionBy<TResult2>(Expression<Func<T, TResult2>> memberExpression);
        IWindowFuncExpressionOver<T, TResult> OrderBy<TResult2>(Expression<Func<T, TResult2>> memberExpression, bool desc);
        IWindowFuncExpressionOver<T, TResult> Option(string option);
        TResult End();
    }
    public interface IWindowFuncExpression<T, TResult> : IQuery
    {
        IWindowFuncExpressionOver<T, TResult> Over();

    }
    public interface IWindowFuncExpression<T>
    {
        /// <summary>
        /// SqlServer row_number() over(option)
        /// </summary>
        /// <returns></returns>
        IWindowFuncExpression<T, long> RowNumber();
        /// <summary>
        /// rank() over(option)
        /// </summary>
        /// <returns></returns>
        IWindowFuncExpression<T, long> Rank();
        /// <summary>
        /// dense_rank() over(option)
        /// </summary>
        /// <returns></returns>
        IWindowFuncExpression<T, long> DenseRank();
        /// <summary>
        /// count(*) over(option)
        /// </summary>
        /// <returns></returns>
        IWindowFuncExpression<T, long> Count();
        IWindowFuncExpression<T, TResult> Sum<TResult>(Expression<Func<T, TResult>> memberExpression);
        IWindowFuncExpression<T, TResult> Avg<TResult>(Expression<Func<T, TResult>> memberExpression);
        IWindowFuncExpression<T, TResult> Max<TResult>(Expression<Func<T, TResult>> memberExpression);
        IWindowFuncExpression<T, TResult> Min<TResult>(Expression<Func<T, TResult>> memberExpression);
    }
    public class WindowFuncExpressionOver<T, TResult> : IWindowFuncExpressionOver<T, TResult>
    {
        //https://www.cnblogs.com/lihaoyang/p/6756956.html

        WindowFuncExpression<T, TResult> parent;
        StringBuilder overStrSub = new StringBuilder();
        internal WindowFuncExpressionOver(WindowFuncExpression<T, TResult> _parent)
        {
            parent = _parent;
        }
        public IWindowFuncExpressionOver<T, TResult> OrderBy<TResult2>(Expression<Func<T, TResult2>> memberExpression, bool desc)
        {
            var field = parent.baseQuery.GetSelectField(true, memberExpression.Body, false).GetQueryFieldString();
            var ord = desc ? "desc" : "asc";
            if(overStrSub.ToString().Contains("order by"))
            {
                overStrSub.Append($",{field} {ord}");
            }
            else
            {
                overStrSub.Append($" order by {field} {ord}");
            }
    
            return this;
        }

        public IWindowFuncExpressionOver<T, TResult> PartitionBy<TResult2>(Expression<Func<T, TResult2>> memberExpression)
        {
            var field = parent.baseQuery.GetSelectField(true, memberExpression.Body, false).GetQueryFieldString();
            overStrSub.Append($" partition by {field}");
            return this;
        }
        public IWindowFuncExpressionOver<T, TResult> Option(string option)
        {
            overStrSub.Append($" {option}");
            return this;
        }
        public TResult End()
        {
            return default(TResult);
        }
        internal string getSql()
        {
            var overStr = overStrSub.ToString();
            return $"over({overStr})";
        }
        public string GetQuery(bool appendOrderBy = true)
        {
            return $"{parent._func} {getSql()}";
        }
    }
    public class WindowFuncExpression<T, TResult> :IWindowFuncExpression<T, TResult>
    {
        internal string _func;
        internal LambdaQueryBase baseQuery;
        public WindowFuncExpression(LambdaQueryBase _baseQuery, string func)
        {
            _func = func;
            baseQuery = _baseQuery;
        }
        WindowFuncExpressionOver<T, TResult> over;
        public IWindowFuncExpressionOver<T, TResult> Over()
        {
            if (over != null)
            {
                throw new Exception("已指定过over");
            }
            over = new WindowFuncExpressionOver<T, TResult>(this);
            return over;
        }
        public string GetQuery(bool appendOrderBy = true)
        {
            return $"";
        }
    }
    public class WindowFuncExpression<T> : IWindowFuncExpression<T>
    {
        LambdaQueryBase baseQuery;
        internal WindowFuncExpression(LambdaQueryBase _baseQuery)
        {
            baseQuery = _baseQuery;
        }
        IWindowFuncExpression<T, TResult> getFunc<TResult>(string func, Expression<Func<T, TResult>> memberExpression)
        {
            var field = baseQuery.GetSelectField(true, memberExpression.Body, false).GetQueryFieldString();
            return new WindowFuncExpression<T, TResult>(baseQuery, $"{func}({field})");
        }
        public IWindowFuncExpression<T, TResult> Avg<TResult>(Expression<Func<T, TResult>> memberExpression)
        {
            return getFunc("avg", memberExpression);
        }

        public IWindowFuncExpression<T, long> Count()
        {
            return new WindowFuncExpression<T, long>(baseQuery, $"count(*)");
        }
        public IWindowFuncExpression<T, long> Rank()
        {
            return new WindowFuncExpression<T, long>(baseQuery, $"rank()");
        }
        public IWindowFuncExpression<T, long> DenseRank()
        {
            return new WindowFuncExpression<T, long>(baseQuery, $"dense_rank()");
        }

        public IWindowFuncExpression<T, TResult> Max<TResult>(Expression<Func<T, TResult>> memberExpression)
        {
            return getFunc("max", memberExpression);
        }

        public IWindowFuncExpression<T, TResult> Min<TResult>(Expression<Func<T, TResult>> memberExpression)
        {
            return getFunc("min", memberExpression);
        }

        public IWindowFuncExpression<T, long> RowNumber()
        {
            return new WindowFuncExpression<T, long>(baseQuery, $"row_number()");
        }

        public IWindowFuncExpression<T, TResult> Sum<TResult>(Expression<Func<T, TResult>> memberExpression)
        {
            return getFunc("sum", memberExpression);
        }
    }
}
